	
  (include-book "io-utilities" :dir :teachpacks)
  (include-book "list-utilities" :dir :teachpacks)
  (include-book "rand" :dir :teachpacks)
  
  ;; converts a list of strings to a list of rational numbers
  (defun strs->rats (strs)
    (if (consp strs)
        (cons (str->rat (car strs)) (strs->rats (cdr strs)))
        nil))
  
  
  (defun getstrings (lines)
    (if (consp lines)
        (cons (chrs->str (car lines)) (getstrings (cdr lines)))
        nil
        )
    )
  
  (defun my-loc-from-file-as-string (str)
    (packets #\Newline (str->chrs str)))
  
  (defun my-loc (file-path state)
    (mv-let (str error state)
            (file->string file-path state)
            (if error
                (mv error state)
                (mv (getstrings (my-loc-from-file-as-string str)) state))))
  
  ; Function to map a letter representing a day of the week
  ; to a number 
  ; U - Sunday    M - Monday    T - Tuesday
  ; W - Wednesday R - Thursday  F - Friday
  ; S - Saturday
  (defun map-day-of-week-to-number (day-of-week)
    (case day-of-week
      (#\U 0)
      (#\M 1)
      (#\T 2)
      (#\W 3)
      (#\R 4)
      (#\F 5)
      (#\S 6)
      (otherwise day-of-week)
      )
    )
  
  ; Input is a character list representing a time
  ; (ie, (#\7 #\: #\3 #\0) is 7:30) returns a 
  ; number representation, (ie, 7:30 is 7.5, 8:00 is 8)
  (defun get-time-as-float (time)
    (let ((hour (chrs->str (car (break-at #\: time))))
          (minutes (chrs->str (cadr (break-at #\: time)))))
      (if (equal hour 12)
          (if (equal minutes ":30")
              (+ 0 .5)
              0
              )
          (if (equal minutes ":30")
              (+ (str->rat hour) .5)
              (str->rat hour)
              )
          )
      )
    )
  
  ; Given a character list of two times seperated
  ; by a tilde (ie, (7:30am~8:30am)) it returns
  ; the distance in 30 minute chunks, the above 
  ; example would return 2
  (defun get-length-of-range (range)
    (let* ((first (car (packets #\~ range)))
           (last (cadr (packets #\~ range)))
           (first-as-number (get-time-as-float (remove #\a (remove #\p (remove #\m first)))))
           (last-as-number (get-time-as-float (remove #\a (remove #\p (remove #\m last))))))
      (if (member #\a first)
          (if (member #\a last)
              (* 2 (- last-as-number first-as-number))
              (* 2 (- (+ 12 last-as-number) first-as-number))
              )
          (* 2 (- last-as-number first-as-number))
          )
      )
    )
  
  ; Helper function for below function
  (defun get-length-and-start-of-range-helper (range)
    (if (member #\a (car (packets #\– range)))
        (cons (get-time-as-float (remove #\a (remove #\p (remove #\m (car (packets #\~ range)))))) 
              (get-length-of-range range))
        (cons (+ 12 (get-time-as-float (remove #\a (remove #\p (remove #\m (car (packets #\~ range)))))))
              (get-length-of-range range))
        )
    )
  
  ; Returns the length of a range along with the start point
  ; (ie, (7:30am~8:30am) returns (7.5 . 2)
  (defun get-length-and-start-of-range (ranges)
    (if (consp ranges)
        (cons (get-length-and-start-of-range-helper (car ranges))
              (get-length-and-start-of-range (cdr ranges)))
        nil
        )                                        
    )
  
  ; Returns a list consisting of the number representation of a
  ; day of the week and the start/length pair (ie,
  ; "M 7:30am~8:30am" returns (1 (7.5 . 2))
  (defun get-day-and-times-pair (student-time-as-chrs)
    (list (map-day-of-week-to-number (car student-time-as-chrs)) 
          (get-length-and-start-of-range (packets #\, (cdr student-time-as-chrs))))
    )
  
  ; Helper function that cycles through the list of student schedules
  ; and calls get-day-and-times-pair on them
  (defun times-from-student-as-str-helper (student-times-as-chrs)
    (if (consp student-times-as-chrs)
        (cons (get-day-and-times-pair (car student-times-as-chrs))
              (times-from-student-as-str-helper (cdr student-times-as-chrs)))
        nil
        )
    )
  
  ; Takes in a list, a start index, and an end index and replaces
  ; all objects in those indices and the ones inbetween with x
  (defun replace (start end x xs)
    (let* ((first (car (break-at-nth start xs)))
           (second (cadr (break-at-nth (+ end 1) xs))))
      (append first (replicate (+ (- end start) 1) x) second)
      )
    )
  
  ; Takes in a day-range pair and replaces that part of the schedule
  ; with 0's
  (defun get-part-of-day (day range array)
    (replace (+ (* day 48) (* 2 (car range))) 
             (- (+ (cdr range) (+ (* day 48) (* 2 (car range)))) 1) 0 array)
    )
  
  ; Helper function for get-schedule-as-bit-array
  (defun get-schedule-as-bit-array-helper (day ranges array)
    (if (consp ranges)
        (get-schedule-as-bit-array-helper day (cdr ranges) (get-part-of-day day (car ranges) array))
        array
        )
    )
  
  ; Cycles through each schedule and creates each students
  ; schedule bit array
  (defun get-schedule-as-bit-array (schedule array)
    (if (consp schedule)
        (get-schedule-as-bit-array (cdr schedule) 
                                   (get-schedule-as-bit-array-helper 
                                    (caar schedule) (cadar schedule) array))
        array
        )
    )
  
  ; Main function for getting a student's schedule as a bit array
  ; initializes the array, packets the student's string and removes
  ; spaces
  (defun schedule-as-bit-array-from-student-as-str (student-as-string)
    (get-schedule-as-bit-array 
     (times-from-student-as-str-helper (packets #\; 
       (remove #\space (cadr (cddddr (packets #\| (str->chrs student-as-string))))))) 
       (replicate (* 48 7) 1))
    )
  
  ; Takes in a filename and outputs its contents as a string
  (defun get-input-file-as-str (input)
    (mv-let (file-as-string error state)
            (file->string input state)
            (map-chrs->str 
             (remove nil (packets-set (list (code-char 10) (code-char 13)) (str->chrs file-as-string))))
            )
    )
  
  ; Takes in a student as a string and converts it into 
  ; usuable data
  ; The format is:
  ; ( "ID" "FirstName LastName" "Gender" "Grade" "Skillsets" (0 0 0 1 0 1 1 1 ...))
  ; Where the bit array is the student's schedule
  (defun student-to-data (student)
    (let ((student-packet (packets #\| (str->chrs student))))
      (if (or (equal (remove #\space (caddr student-packet)) (list #\M)) 
              (equal (remove #\space (caddr student-packet)) (list #\m)))
          (list (chrs->str (car student-packet))  (chrs->str (cadr student-packet)) "m" 
                (str->rat (chrs->str (cadddr student-packet)))
                (string-downcase (chrs->str (car (cddddr student-packet))))
                (schedule-as-bit-array-from-student-as-str student))
          (list (chrs->str (car student-packet))  (chrs->str (cadr student-packet)) "f" 
                (str->rat (chrs->str (cadddr student-packet))) 
                (string-downcase (chrs->str (car (cddddr student-packet))))
                (schedule-as-bit-array-from-student-as-str student))
      )
    )
  )
  
  ; Cycles through the list of students and turns them into usable data
  (defun get-students-as-data-helper (students)
    (if (consp students)
        (cons (student-to-data (car students)) 
              (get-students-as-data-helper (cdr students)))
        nil
        )
    )
  
  ; Main function for student-strings into data, input is the filename
  (defun get-students-as-data (input)
    (get-students-as-data-helper (cdr (get-input-file-as-str input)))
    )
  
  ;gets rid of extra spaces and punctuation between words 
  (defun strs-join (glue strs)
    (if(endp strs)
       ""
       (if (endp (cdr strs))
           (car strs)
           (concatenate 'string
                        (car strs)
                        glue
                        (strs-join glue (cdr strs)))
           )))
  
  ;gets ride of white space thatis in a single string 
  (defun trim-stringss (ss)
    (if (endp ss) ss
        (cons 
         (strs-join " " (words (car ss))) 
         (trim-stringss (cdr ss)))))
  
  (defun str->rat-caar (tr)
    (cons (cons (str->rat(car (car tr)))
                (cdr (car tr)))
          (cdr tr)))
  
  ;gets the five numbers at the begining of the specification line (#teams & weights) 
  ;packets remainder of string by semi-colon 
  (defun input-parse-helper (xs)
    (if (equal (len xs) 6)
    (cons (strs->rats (trim-stringss (take 5 xs)))
         (car (nthcdr 5 xs)))
    "Error!"))
  
  ;packets the string at |  
  (defun parse-input (file-path)
    (input-parse-helper 
              (getstrings (packets #\| (str->chrs (car (get-input-file-as-str file-path))))))
  )
  
  ;gets number of teams user wants to have
  (defun get-num-teams (xs)
    (caar xs)
    )
  
  (defun get-sum-of-weights (weights acc)
    (if (consp weights)
        (get-sum-of-weights (cdr weights) (+ (car weights) acc))
        acc
    )
  )
  
  (defun get-weights  (xs)
    (let ((sum-of-weights (get-sum-of-weights (cdar xs) 0)))
      (list (/ (cadar xs) sum-of-weights) (/ (caddar xs) sum-of-weights)
            (/ (car (cdddar xs)) sum-of-weights) (/ (cadr (cdddar xs)) sum-of-weights))
    )
  ) 

  ;gets all skill 
  (defun get-skill-sets (xs)
    (cdr(car xs))
    )
  
  ; Returns each member's ID and name
  (defun member-output (members)
    (if (consp members)
        (concatenate 'string (caar members) "    " (cadar members) "\n\t\t" (member-output (cdr members)))
        ""
    )
  )
  
  ; Runs through each group and passes its members to member-output
  (defun output-helper (group)
    (concatenate 'string "Group " (int->str (caar group)) "    Score: " (rat->str (cadar group) 3) "\n\t\t"
                 (member-output (cdr group)) "\n\t")
  )
  
  ; Main output function, given a flattened avl tree (with flattened avl tree data)
  ; it returns a string to write to a file
  (defun output-groups (flattened-tree)
    (if (consp flattened-tree)
        (string-append (output-helper (car flattened-tree)) (output-groups (cdr flattened-tree)))
        ""
    )
  )
  
  (defun output (specs flattened-tree)
    (let ((sum-of-weights (get-sum-of-weights (get-weights specs) 0))
          (weights (get-weights specs)))
         (concatenate 'string "Team Set Criteria:\n\tGrade (" (rat->str (/ (car weights) sum-of-weights) 3) 
                      ")\n\tSkill sets (" (rat->str (/ (cadddr weights) sum-of-weights) 3) ")\n\tSchedule ("
                      (rat->str (/ (cadr weights) sum-of-weights) 3) ")\n\tGender (" 
                      (rat->str (/ (caddr weights) sum-of-weights) 3) ")\n\nSuggested Team Set:\n\t" 
                      (output-groups flattened-tree))
    )
  )
  
  ;; BLARGH FIX THIS PART NEXT
  (defun ith-seed (seed i)
    (if (equal i 0)
        seed
        (ith-seed (next-seed seed) (- i 1))
        )
    )
  
  ; Iterates 50 fifty times to find the best tree
  (defun iterate-trees (students num-teams weights specifications seed i current-best-tree)
    (if (< i 50)
        (let* ((current-tree 
                (form-initial-tree students num-teams weights specifications (ith-seed seed i))))
          (if (> (cadaar current-tree) (cadaar current-best-tree))
              (iterate-trees students num-teams weights specifications seed (+ i 1) current-tree)
              (iterate-trees students num-teams weights specifications seed (+ i 1) current-best-tree)
              )
          )
        current-best-tree
        )
    )
 
  (defun combine-again (names weights sum)
    (if (consp names)
        (cons (list (car names) (/ (car weights) sum))
              (combine-again (cdr names) (cdr weights) sum))
        nil
    )
  )
  
 (defun get-pair-of-weight-and-name-helper (name-weight-pairs)
   (let ((weights (strip-cdrs name-weight-pairs))
          (names (strip-cars name-weight-pairs)))
         (combine-again names weights (get-sum-of-weights weights 0))
   )
 )
  
 (defun get-pair-of-weight-and-name (skill-sets)
   (if (consp skill-sets)
       (let* ((skill-name-and-weight (car (packets #\: (car skill-sets))))
              (skill-name-and-weight-list (packets #\, skill-name-and-weight)))
             (cons (cons (chrs->str (cadr skill-name-and-weight-list))
                   (str->rat (chrs->str (remove #\space (car skill-name-and-weight-list)))))
                   (get-pair-of-weight-and-name (cdr skill-sets)))
       )
       nil
   )
 )
 
 (defun to-strings (xs)
   (if (consp xs)
       (cons (chrs->str (car xs)) (to-strings (cdr xs)))
       nil
   )
 )
 
 (defun get-individual-skills (name-weight-pairs skill-sets)
   (if (consp skill-sets)
       (let* ((skills (cadr (packets #\: (car skill-sets))))
              (indiv-skill-sets (packets #\, skills)))
         (cons (append (car name-weight-pairs) (list (to-strings indiv-skill-sets)))
               (get-individual-skills (cdr name-weight-pairs) (cdr skill-sets)))
       )
       nil
    )     
 )
        
  (defun skill-set-splitting (skills)
    (let* ((skill-sets (packets #\; (str->chrs skills))))
      (get-individual-skills (get-pair-of-weight-and-name-helper (get-pair-of-weight-and-name skill-sets))
                             skill-sets)
    )
  )
  
  ;takes in the input file and writes out the output file
  (defun team-creator (input-file output-file) 
    (let ((specs (parse-input input-file)))
      (if (not (stringp specs))
          (iterate-trees (get-students-as-data input-file) (get-num-teams specs) (get-weights specs)
                         (skill-set-splitting (string-downcase (cdr specs))) (initial-seed) 0
                         (form-initial-tree (get-students-as-data input-file) (get-num-teams specs) 
                             (get-weights specs) (skill-set-splitting (string-downcase (cdr specs))) 
                             (initial-seed)))
           specs
      )
    )
  )